package com.virus.server.http;

import com.virus.server.catalina.Context;
import com.virus.server.catalina.Engine;
import com.virus.server.catalina.Host;
import com.virus.server.utils.ServerXmlUtils;
import com.virus.server.utils.StringUtils;

import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.*;

public class Request extends BaseRequest {

    private Socket socket;

    private InputStream inputStream;

    private String request;

    private String requestLine;

    private String protocol;

    private String method;

    private String requestURI;

    private String requestHead;

    private String servletPath;

    private String contextPath;

    private Engine engine;

    private Host host;

    private Context context;

    private Cookie[] cookies;

    private HttpSession session;

    private Map<String, String> requestHeadersMap = new HashMap<>();

    public Request(Socket socket, Engine engine) {
        this.socket = socket;
        this.engine = engine;
        try {
            this.inputStream = socket.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        parseRequest(inputStream);
        if (!StringUtils.isEmpty(request)) {
            parseRequestLine();
            parseRequestHead();
            parseContextPath();
            parseServerPath();
            parseHost();
            parseContext();
        }
    }

    private void parseRequest(InputStream inputStream) {
        try {
            byte[] bytes = new byte[1024];
            int read = inputStream.read(bytes);
            this.request = new String(bytes, 0, read);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void parseRequestLine() {
        int i = request.indexOf(System.lineSeparator());
        requestLine = request.substring(0, i);
        String[] element = requestLine.split(" ");
        protocol = element[2];
        method = element[0];
        requestURI = element[1];
        if (requestURI.contains("?")) {
            int j = requestURI.indexOf("?");
            requestURI = requestURI.substring(0, j);
        }
    }

    private void parseRequestHead() {

        int start = request.indexOf(System.lineSeparator());
        int end = request.indexOf(System.lineSeparator() + System.lineSeparator());
        requestHead = request.substring(start + 2, end);
        String[] lines = requestHead.split(System.lineSeparator());
        for (String line : lines) {
            int i = line.indexOf(":");
            String key = line.substring(0, i);
            String value = line.substring(i + 1);
            requestHeadersMap.put(key.trim(), value.trim());
        }
    }

    private void parseContextPath() {
        String path = StringUtils.subString(requestURI, "/", "/");
        if (path == null && !requestURI.contains(".")) {
            contextPath = requestURI;
            return;
        }
        if (path == null) {
            contextPath = "/";
            return;
        }
        contextPath = "/" + path;
    }

    private void parseServerPath() {
        if (contextPath.equals("/")) {
            servletPath = requestURI;
            return;
        }
        servletPath = requestURI.substring(contextPath.length());
    }

    private void parseHost() {
        String host = requestHeadersMap.get("Host");
        List<Host> hosts = engine.getHosts();
        for (Host h : hosts) {
            String name = h.getName();
            if (name.equals(host)) {
                this.host = h;
                return;
            }
        }
        String defaultHost = engine.getDefaultHost();
        for (Host h : hosts) {
            if (h.getName().equals(defaultHost)) {
                this.host = h;
                return;
            }
        }
    }

    private void parseContext() {
        context = ServerXmlUtils.getContextMap().get(contextPath);
    }

    private void parseCookie() {
        List<Cookie> cookies = new ArrayList<>();
        String cookie = getHeader("Cookie");
        if (cookie != null) {
            String[] parts = cookie.split(";");
            for (String part : parts) {
                String[] split = part.split("=");
                Cookie c = new Cookie(split[0].trim(), split[1].trim());
                cookies.add(c);
            }
            this.cookies = new Cookie[cookies.size()];
            for (int i = 0; i < cookies.size(); i++) {
                this.cookies[i] = cookies.get(i);
            }
        }
    }

    public String getRequest() {
        return request;
    }

    public String getRequestLine() {
        return requestLine;
    }

    public String getProtocol() {
        return protocol;
    }

    public String getMethod() {
        return method;
    }

    public String getRequestURI() {
        return requestURI;
    }

    public String getRequestHead() {
        return requestHead;
    }

    public String getHeader(String key) {
        return requestHeadersMap.get(key);
    }

    public String getServletPath() {
        return servletPath;
    }

    public String getContextPath() {
        return contextPath;
    }

    public Context getContext() {
        return context;
    }

    public String getRemoteAddr() {
        return socket.getInetAddress().getHostAddress();
    }

    public ServletContext getServletContext() {
        return context.getServletContext();
    }

    @Override
    public Cookie[] getCookies() {
        return cookies;
    }

    public String getJsessionidFromCookie() {
        if (cookies == null) {
            return null;
        }
        for (Cookie cookie : this.cookies) {
            if ("JSESSIONID".equals(cookie.getName())) {
                return cookie.getValue();
            }
        }
        return null;
    }

    @Override
    public HttpSession getSession() {
        return session;
    }

    public void setSession(HttpSession session) {
        this.session = session;
    }
}
